#pragma once
#include<pthread.h>
#include<queue>


int defaultcapacity=20;//设置最大容量为20

template<class T>
class BlockingQueue
{
    public:
    BlockingQueue( int capacity=defaultcapacity)
    :max_capacity(capacity)
    {
        //局部的锁跟条件变量需要初始化
        pthread_mutex_init(&mutex_,nullptr);//第二个参数设置null
        pthread_cond_init(&consumer_cond_,nullptr);
        pthread_cond_init(&producter_cond_,nullptr);
    }
    T Pop()
    {
        Lock(&mutex_);//这个过程相当于买票
        //进入到这里就是临界区
        //买票了还不一定有座位，如果有座位可以直接进，如果没座位需要在外面排队
        //在临界区任一时刻被切换下去都没事，因为它带着锁离开的，就算我不在其他线程也无法获取锁无法进来
        
        while(blocking_queue.empty())//为了避免线程被伪唤醒，以防万一，醒来第一件事再判断一次
        pthread_cond_wait(&consumer_cond_,&mutex_); //获取数据的是消费者，进入等待队列立即休眠这个线程不会往后执行，等待被唤醒，同时把身上的锁解开，当再次被唤醒时重新获取锁
        
        //走到这一步说明条件满足可以消费数据了
        //获取数据-消费数据-删除队头
        T task=blocking_queue.front();
        blocking_queue.pop();
        if(blocking_queue.size()<=max_capacity*1/5);
        pthread_cond_signal(&producter_cond_);
        UnLock(&mutex_);
        return task;//确保第一个拿到锁的人第一个出来
    }
    void Push(const T& task)
    {
        //生产者push数据进来
        Lock(&mutex_);
        //这里进入临界区
        //判断临界区资源是否就绪
        while(blocking_queue.size()==max_capacity)//如果数据满了就不能再放数据，需要排队等待，如何为满？这个最大容量值需要自己设置？
        {
            pthread_cond_wait(&producter_cond_,&mutex_);
        }
        //来到这里说明临界区资源已满足
        blocking_queue.push(task);
        //生产数据后唤醒消费者来消费，可以根据某个条件来唤醒
        if(blocking_queue.size()>max_capacity*4/5)//数据存在个数达到最大容量的2/3唤醒消费者
        pthread_cond_signal(&consumer_cond_);
        UnLock(&mutex_);
    }
    ~BlockingQueue()
    {
        //局部的锁跟条件变量需要销毁
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&producter_cond_);
        pthread_cond_destroy(&consumer_cond_);
    }
    private:
    int Lock(pthread_mutex_t*mutex)
    {
       return pthread_mutex_lock(mutex);
    }
    int UnLock(pthread_mutex_t* mutex)
    {
        return pthread_mutex_unlock(mutex);
    }
    int max_capacity;
    std::queue<T> blocking_queue;//基于阻塞队列的生产消费者模型
    pthread_mutex_t mutex_;//为了解决多线程并发引发的数据不一致的问题需要上锁
    pthread_cond_t consumer_cond_;//为了解决竞争锁引发的线程饥饿问题需要条件变量（在保证数据安全的前提下，让线程有序的访问资源（同步））
    pthread_cond_t producter_cond_;//生产消费者都需要一个等待队列（生产者到生产者的等待队列，消费者到消费者的等待队列）
};